﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Sublimation
{
    public class Unit : GameObject
    {
        protected bool attacking;
        protected Shield shield;
        public Shield Shield
        {
            get { return this.shield; }
        }

        protected bool goArround;
        protected bool goLeft;

        protected float life;
        public float Life
        {
            get
            {
                return this.life;
            }
            set
            {
                this.life = MathHelper.Clamp(value,0, MaxLife);
            }
        }

        protected float maxLife;
        public float MaxLife
        {
            get
            {
                return this.maxLife;
            }
            set
            {
                this.maxLife = value;
            }
        }

        protected float meleeDamage;
        public float MeleeDamage
        {
            get { return this.meleeDamage; }
            set { this.meleeDamage = value; }
        }

        protected int meleeAttackDelay;
        public int MeleeAttackDelay
        {
            get { return this.meleeAttackDelay; }
            set { this.meleeAttackDelay = value; }
        }

        public Unit(WorldProperties worldProperties)
            : base(worldProperties)
        {
            attacking = false;
           
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            // Does it survive this update?
            // die if there isnt enough health and still alive
            if (Enabled && Life == 0 )
            {
                Kill();
                return;
            }

            // update movement etc
            // TODO: have this move normally and collide, turn at speed etc

            // update melee timer
            meleeAttackTimerMillis = Math.Max(0, meleeAttackTimerMillis - gameTime.ElapsedGameTime.TotalMilliseconds);
        }

        public override void DoMovement(GameTime gameTime)
        {
            base.DoMovement(gameTime);

            this.Rotation = (float)Math.Atan2(Velocity.Y, Velocity.X);

            // check if the velocity is NaN (very small chance this can happen when a zero length vector is normalised)
//            if (!(float.IsNaN(Velocity.X) || float.IsNaN(Velocity.Y)))
//                this.Rotation = (float)Math.Acos(Vector2.Dot(Vector2.UnitX, Velocity) / Velocity.Length()) * Math.Sign(Vector2.Dot(Vector2.UnitY, Velocity));
//#if DEBUG
  //          else System.Diagnostics.Debug.WriteLine("Trying to rotate a NaN"); // this is because its normally a code error when velocity is NaN
//#endif
        }

        public override void ApplyDamage(float damage)
        {
            base.ApplyDamage(damage);

            // remove damage from shield if there is any
            if (Shield != null && Shield.ShieldEnergy > 0)
            {
                Shield.ApplyDamage(damage);
            }
            else
            {
                Life -= damage;
                if (Enabled && Life == 0)
                {
                    Kill();
                }
            }

            // draw a blood splatter on the map when the unit takes damage
            worldProperties.DrawBloodSplatter(damage, this);
        }

        public override void Kill()
        {
            base.Kill();

            // cleanup the components
            if (Shield != null) Shield.Kill();

            //TODO: an explosion and maybe dying sound (SPLAT)
        }

        public override void CheckCollisions(GameTime gameTime)
        {
            base.CheckCollisions(gameTime);

            bool collideWithObject = false;
            // die if the unit leaves the screen
            if (!worldProperties.Map.WorldBounds.Intersects(BoundingRectangle))
            {
                Kill();
                return;
            }

            //physical ojbect interaction not working properly yet
            /*
            foreach (Rectangle rectangle in worldProperties.Map.PhysicalObjects)
            {
                if (rectangle.Intersects(BoundingRectangle))
                {
                    collidePhysicalObject(rectangle,gameTime);
                    collideWithObject = true;
                }
            }
             */
            goArround = collideWithObject; 

            // Towers
            foreach (Player player in worldProperties.Players)
            {
                for(int i=0;i<player.Towers.Count;i++){
                    if (player.Towers[i]!= null && CollidesWith(player.Towers[i])) collideWithTower(player.Towers[i], gameTime);
                }
            }
        }

        protected virtual void collidePhysicalObject(Rectangle physicalObject, GameTime gameTime)
        {
            //XXX: this is a bit of a performance hit
            // behaviour for hitting a tower
            while(boundingRectangle.Intersects(physicalObject)){
                Position -= Vector2.Normalize(Velocity);
            }
            if(!goArround){
                goArround = true;
 
            }
            if (Position.X < physicalObject.X)
            {
                if(goLeft){
                    Velocity = new Vector2(0, -1) * Velocity.Length();
                }else{
                     Velocity = new Vector2(0, 1) * Velocity.Length();
                }
            }
            else if(Position.X > physicalObject.X+physicalObject.Width)
            {
                if (goLeft)
                {
                    Velocity = new Vector2(0, 1) * Velocity.Length();
                }
                else
                {
                    Velocity = new Vector2(0, -1) * Velocity.Length();
                }
            }
            else if (Position.Y < physicalObject.Y)
            {
                if (goLeft)
                {
                    Velocity = new Vector2(1, 0) * Velocity.Length();
                }
                else
                {
                    Velocity = new Vector2(-1, 0) * Velocity.Length();
                }
            }
            else
            {
                if (goLeft)
                {
                    Velocity = new Vector2(1, 0) * Velocity.Length();
                }
                else
                {
                    Velocity = new Vector2(-1, 0) * Velocity.Length();
                }
            }
        }

        protected virtual void collideWithTower(Tower tower, GameTime gameTime)
        {
            //XXX: this is a bit of a performance hit
            // behaviour for hitting a tower
            Position = Position + Vector2.Normalize(Velocity) * (Vector2.Distance(Position, tower.Position) - CollisionRadius - tower.CollisionRadius); 
        }

        private double meleeAttackTimerMillis = 0;
        protected virtual void MeleeAttack(GameObject target)
        {
            attacking = true;
            if (meleeAttackTimerMillis == 0)
            {
                target.ApplyDamage(MeleeDamage);
                meleeAttackTimerMillis = MeleeAttackDelay;
            }
        }

        protected const float UnitAlignBehaviourWeight = 0.3f;
        protected const float UnitCohesionBehaviourWeight = 0.3f;
        protected const float SeparationDistance = 80.0f;
        protected const float UnitSeparationBehaviourWeight = 0.8f;
        protected const float UnitInertiaBehaviourWeight = 0.25f;
        protected const float UnitRandomBehaviourWeight = 0.05f;
        protected override void SetupBehaviours()
        {
            base.SetupBehaviours();

            this.behaviours.Add(new Behaviours.UnitAlignBehaviour(worldProperties, this, UnitAlignBehaviourWeight));
            this.behaviours.Add(new Behaviours.UnitCohesionBehaviour(worldProperties, this, UnitCohesionBehaviourWeight, SeparationDistance));
            this.behaviours.Add(new Behaviours.UnitInertiaBehaviour(worldProperties, this, UnitInertiaBehaviourWeight));
            this.behaviours.Add(new Behaviours.UnitRandomBehaviour(worldProperties, this, UnitRandomBehaviourWeight));
            this.behaviours.Add(new Behaviours.UnitSeparationBehaviour(worldProperties, this, UnitSeparationBehaviourWeight, SeparationDistance));
        }
    }
}